package org.apache.struts.ehr.medical_records.action;

import java.util.ArrayList;
import java.util.Date;
import java.util.Map;

import org.apache.axiom.om.OMElement;
import org.apache.struts.ehr.medical_records.model.Bht;
import org.apache.struts.ehr.medical_records.model.GuardianForPatientDetails;
import org.apache.struts.ehr.medical_records.model.HL7Message;
import org.apache.struts.ehr.medical_records.model.MedicalRecords;
import org.apache.struts.ehr.medical_records.model.PatientRecords;
import org.apache.struts.ehr.medical_records.model.Query;
import org.apache.struts.ehr.medical_records.model.ReplyMessage;
import org.apache.struts.ehr.patient_details.model.Patient;
import org.apache.struts.ehr.patient_details.model.PatientDetails;

import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.model.DataTypeException;
import ca.uhn.hl7v2.model.v251.datatype.XAD;
import ca.uhn.hl7v2.model.v251.datatype.XTN;
import ca.uhn.hl7v2.model.v251.message.ADR_A19;
import ca.uhn.hl7v2.model.v251.segment.DG1;
import ca.uhn.hl7v2.model.v251.segment.NK1;
import ca.uhn.hl7v2.model.v251.segment.OBX;
import ca.uhn.hl7v2.model.v251.segment.PID;
import ca.uhn.hl7v2.model.v251.segment.PV1;
import ca.uhn.hl7v2.model.v251.segment.PV2;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.earogya.Constants;
import com.earogya.logic.medical_records.CreateADT01;
import com.earogya.logic.medical_records.CreateADTA08;
import com.earogya.logic.medical_records.CreateQRYA19;
import com.earogya.logic.medical_records.ParseReply;
import com.earogya.communicate.Communicator;

/***
 * This class is the struts action to add medical records for a given patient
 * @author Chamendri
 *
 */
public class MedicalRecordsAction extends ActionSupport{

	private MedicalRecords medicalRecords;
	private HL7Message message;
	private ReplyMessage replyMessage;
	private PatientRecords records;
	private Patient details;
	
	public Patient getDetails() {
		return details;
	}

	public ReplyMessage getReplyMessage() {
		return replyMessage;
	}

	public PatientRecords getRecords() {
		return records;
	}
	
	public HL7Message getMessage() {
		return message;
	}

	public MedicalRecords getMedicalRecords() {
		return medicalRecords;
	}

	public void setMedicalRecords(MedicalRecords medicalRecords) {
		this.medicalRecords = medicalRecords;
	}

	/**
	 * Overrides execute method in ActionSupport interface
	 * creates an ADT A08 meesage and send to the back end.
	 * Parse the reply return the value SUCCESS or ERROR accordingly.
	 */
	public String execute() throws Exception {
		Map<String, Object> session=ActionContext.getContext().getSession();
		replyMessage=new ReplyMessage();
		replyMessage.setMessage(Constants.EMPTY);
		CreateADTA08 createMessage=new CreateADTA08(medicalRecords);
		String msg=createMessage.createMessage(Constants.SENDING_FACILITY);
		message=new HL7Message();
		if(!msg.equals(null)){
			Communicator c=new Communicator();
			String reply=c.sendMessage(msg);
			if(!reply.equals(Constants.NO_REPLY)){
				ParseReply parse=new ParseReply();
				replyMessage=(ReplyMessage)parse.parseMessage(reply);
				if(replyMessage.getMessage().equals(Constants.SUCCESS)){
					Query query=new Query();
					query.setPatientId(medicalRecords.getPatientId());
					query.setSearch_type(Constants.ADMISSION);
					CreateQRYA19 createQuery=new CreateQRYA19(query);
					String message=createQuery.createMessage(Constants.SENDING_FACILITY);
					if(!message.equals(null)){
						Communicator com=new Communicator();
						String replyQuery=com.sendMessage(message);
						if(!replyQuery.equals(Constants.NO_REPLY)){
							ParseReply parseReply=new ParseReply();
							try{
								ADR_A19 adr=(ADR_A19)parseReply.parseMessage(replyQuery);
								this.setValuesToAdmission(adr);
								PatientDetails patient_detials=new PatientDetails();
								patient_detials.getPatient().setPatientId(records.getPatient_id());
								com.earogya.logic.patient_details.CreateQRYA19 qry=new com.earogya.logic.patient_details.CreateQRYA19(patient_detials);
								String msg_patient_details=qry.createMessage(Constants.SENDING_FACILITY);
								if(!msg_patient_details.equals(null)){
									Communicator com1=new Communicator();
									String reply1=com1.sendMessage(msg_patient_details);
									if(!reply1.equals(Constants.NO_REPLY)){
										ParseReply parse1=new ParseReply();
										try{
											ADR_A19 adr1=(ADR_A19)parse1.parseMessage(reply1);
											this.setValuestoPatientDetails(adr1);
										}catch(ClassCastException e){
											replyMessage=(ReplyMessage)parse.parseMessage(reply1);
											return ERROR;
										}
									}else{
										replyMessage.setMessage(reply1);
										return ERROR;
									}
								}else{
									replyMessage.setMessage("Error while creating the message");
									return ERROR;
								}
							}catch(ClassCastException e){
								replyMessage=(ReplyMessage)parse.parseMessage(reply);
								return ERROR;
							}
						}else{
							replyMessage.setMessage(reply);
							return ERROR;
						}
					}else{
						replyMessage.setMessage("Error while creating the message");
						return ERROR;
					}
				}else{
					return ERROR;
				}
			}else{
				replyMessage.setMessage(reply);
				return ERROR;
			}
		}
		replyMessage.setMessage("empty");
		return SUCCESS;
	}
	
	/***
	 * Parse the ADR A19 message and add the values to a PatientRecords object.
	 * @param adr
	 * @throws HL7Exception
	 */
	private void setValuesToAdmission(ADR_A19 adr) throws HL7Exception{
		this.records=new PatientRecords();
		PID pid=adr.getQUERY_RESPONSE().getPID();
		records.setPatient_id(pid.getPatientID().getIDNumber().getValue());
		
		PV1 pv1=adr.getQUERY_RESPONSE().getPV1();
		records.setAdmission_date(pv1.getAdmitDateTime().getTime().getValueAsDate());
		records.setAdmiting_officer(pv1.getAdmittingDoctor(0).getIDNumber().getValue());
		records.setBed(pv1.getPriorPatientLocation().getBed().getValue());
		records.setWard(pv1.getPriorPatientLocation().getRoom().getValue());
		Bht bht=new Bht();
		bht.setCurrent_bed(pv1.getAssignedPatientLocation().getBed().getValue());
		bht.setCurrent_ward(pv1.getAssignedPatientLocation().getRoom().getValue());
		bht.setConsultant_id(pv1.getConsultingDoctor(0).getIDNumber().getValue());
		records.setBht(bht);
		
		PV2 pv2=adr.getQUERY_RESPONSE().getPV2();
		records.setAdmit_reason(pv2.getAdmitReason().getText().getValue());
		
		int numberOfObservations=adr.getQUERY_RESPONSE().getOBXReps();
		int numberOfDiagonosis=adr.getQUERY_RESPONSE().getDG1Reps();
		int maxLength=numberOfDiagonosis;
		if(numberOfObservations>numberOfDiagonosis){
			maxLength=numberOfObservations;
		}
		ArrayList<MedicalRecords> medicalRecordsList=new ArrayList<MedicalRecords>();
		for(int i=0; i<maxLength; i++){
			MedicalRecords medicalRecords=new MedicalRecords();
			if(i<numberOfObservations){
				OBX obx=adr.getQUERY_RESPONSE().getOBX(i);
				medicalRecords.setObservation(obx.getObservationValue(0).getData().encode());
				medicalRecords.setObserverId(obx.getResponsibleObserver(0).getIDNumber().getValue());
				medicalRecords.setObservationdate(obx.getDateTimeOfTheObservation().getTime().getValueAsDate());
			}
			else{
				medicalRecords.setObservation("");
				medicalRecords.setObserverId("");
			}
			if(i<numberOfDiagonosis){
				DG1 dg1=adr.getQUERY_RESPONSE().getDG1(i);
				medicalRecords.setDiagosis(dg1.getDiagnosisDescription().getValue());
				medicalRecords.setDiagnosingClinicianId(dg1.getDiagnosingClinician(0).getIDNumber().getValue());
				medicalRecords.setDiagnosingDate(dg1.getDiagnosisDateTime().getTime().getValueAsDate());
			}
			else{
				medicalRecords.setDiagosis("");
				medicalRecords.setDiagnosingClinicianId("");
			}
			medicalRecordsList.add(medicalRecords);
		}
		records.setMedicalRecords(medicalRecordsList);
		
		GuardianForPatientDetails guardian=new GuardianForPatientDetails();
		NK1 nk1=adr.getQUERY_RESPONSE().getNK1();
		guardian.setPrefix(nk1.getNK1Name(0).getPrefixEgDR().getValue());
		guardian.setGiven_name(nk1.getNK1Name(0).getGivenName().getValue());
		guardian.setFurthur_names(nk1.getNK1Name(0).getSecondAndFurtherGivenNamesOrInitialsThereof().getValue());
		guardian.setSurname(nk1.getNK1Name(0).getFamilyName().getSurname().getValue());
		guardian.setNic_no(nk1.getContactPersonSocialSecurityNumber().getValue());
		guardian.setGuardian_relationship(nk1.getRelationship().getText().getValue());
		
		ArrayList<org.apache.struts.ehr.medical_records.model.Address> addressList=new ArrayList<org.apache.struts.ehr.medical_records.model.Address>();
		XAD[] addresses=nk1.getAddress();
		for(int i=0; i<addresses.length; i++){
			org.apache.struts.ehr.medical_records.model.Address address=new org.apache.struts.ehr.medical_records.model.Address();
			address.setHouse_number(addresses[i].getStreetAddress().getDwellingNumber().getValue());
			address.setStreet(addresses[i].getStreetAddress().getStreetName().getValue());
			address.setCity_1(addresses[i].getOtherDesignation().getValue());
			address.setCity_2(addresses[i].getCity().getValue());
			address.setCountry(addresses[i].getCountry().getValue());
			addressList.add(address);
		}
		guardian.setAddresses(addressList);
		
		ArrayList<String> telephoneNumberList=new ArrayList<String>();
		ArrayList<String> emailList=new ArrayList<String>();
		XTN[] telephoneNumbers=nk1.getPhoneNumber();
		for(int i=0; i<telephoneNumbers.length; i++){
			telephoneNumberList.add(telephoneNumbers[i].getTelephoneNumber().getValue());
			if(telephoneNumbers[i].getEmailAddress()!=null){
				emailList.add(telephoneNumbers[i].getEmailAddress().getValue());
			}
		}
		guardian.setTelephoneNumbers(telephoneNumberList);
		guardian.setEmail(emailList);
		records.setGuardian(guardian);
	}
	
	/***
	 * Parse the ADR A19 message and add the values to a Patient object.
	 * @param adr
	 * @throws DataTypeException
	 */
	private void setValuestoPatientDetails(ADR_A19 adr) throws DataTypeException{
		this.details=new Patient();
		PID pid=adr.getQUERY_RESPONSE().getPID();
		if(pid.getDateTimeOfBirth().getTime().getValue()==null){}
		else{
			details.setBirthTime(pid.getDateTimeOfBirth().getTime().getValueAsDate());
		}
		details.setGender(pid.getAdministrativeSex().getValue());
		String prefix=pid.getPatientName(0).getPrefixEgDR().getValue();
		String givenName=pid.getPatientName(0).getGivenName().getValue();
		String familyName=pid.getPatientName(0).getFamilyName().getSurname().getValue();
		if(prefix==null || prefix.equals("null")){
			prefix="";
		}
		if(givenName==null || givenName.equals("null")){
			givenName="";
		}
		if(familyName==null || familyName.equals("null")){
			familyName="";
		}
		details.setGivenName(prefix+" "+givenName+" "+familyName);
		
	}
	
	/**
	 * Overrides validate method in ActionSupport interface
	 * validates the input fields in add medical records form.
	 * Either observation or diagnosis can be empty.
	 */
	public void validate(){
		if(medicalRecords.getPatientId().length()==0){
			addFieldError("medicalRecords.patientId", "This field is required");
		}else if(!this.isInteger(medicalRecords.getPatientId())){
			addFieldError("medicalRecords.patientId", "Incorrect ID format");
		}
		if(medicalRecords.getObservation().length()==0 && medicalRecords.getDiagosis().length()==0){
			addFieldError("medicalRecords.observation", "Either diagosis or observation or both is reuqired");
			addFieldError("medicalRecords.diagosis", "Either diagosis or observation or both is reuqired");
		}
		if(medicalRecords.getObservation().length()>0){
			if(medicalRecords.getObserverId().length()==0){
				addFieldError("medicalRecords.observerId", "This field is required");
			}
			if(medicalRecords.getObservationdate()==null){
				addFieldError("medicalRecords.observationdate", "This field is required");
			}
		}
		if(medicalRecords.getDiagosis().length()>0){
			if(medicalRecords.getDiagnosingClinicianId().length()==0){
				addFieldError("medicalRecords.diagnosingClinicianId", "This field is required");
			}
			if(medicalRecords.getDiagnosingDate()==null){
				addFieldError("medicalRecords.diagnosingDate", "This field is required");
			}
		}
	}
	
	/**
	 * check if the given string is an integer.
	 * @param value :given string
	 * @return true if the given value is an integer
	 */
	private Boolean isInteger(String value){
		Boolean isInteger=false;
		try{
			int intValue=Integer.parseInt(value);
			isInteger=true;
		}catch(NumberFormatException e){
			isInteger=false;
		}
		return isInteger;
	}
}
